import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression # 逻辑回归
from sklearn.metrics import accuracy_score # 评估
from sklearn.model_selection import GridSearchCV # 交叉验证和网格搜索

if __name__ == '__main__':
    # 1- 数据准备
    df = pd.read_csv("breast-cancer-wisconsin.csv",encoding="UTF-8")
    # df.info()

    # 2- 数据基本处理
    # 2.1- 将问号替换为NaN值
    # inplace=True：直接对原始数据内容修改。默认是False
    df.replace("?",np.NAN,inplace=True)
    # df.info()

    # 2.2- 将包含NaN值的样本数据删除
    df.dropna(inplace=True)
    # df.info()

    # 2.3- 将整个数据分为特征数据和目标数据
    x = df.iloc[:, 1:-1] # 删除前后两列
    y = df.iloc[:, -1]

    # 3- 特征工程，标准化处理
    # 3.1- 对样本数据进行划分，为训练集和测试集
    x_train,x_test,y_train,y_test = train_test_split(x,y,test_size=0.3,random_state=56)

    # 3.2- 标准化处理
    # 3.1- 创建标准化的实例对象
    transformer = StandardScaler()

    # 3.2- 对训练集数据进行先学习规则（均值、标准差），然后再对训练集进行处理
    new_x_train = transformer.fit_transform(x_train)

    # 4- 创建机器学习实例对象：交叉验证和网格搜索
    # 4.1- 实例化对象
    estimator = LogisticRegression()

    # 4.2- 创建交叉验证和网格搜索
    """
        交叉验证和网格搜索的回顾
            1- 交叉验证：数据集划分的一种方式
            2- 网格搜索：从众多的参数中，找出最优的超参数
            3- 参数解释
                3.1- estimator：原始的算法模型
                3.2- param_grid：超参数的选择范围
                3.3- cv：交叉验证中将数据分为几份/几折。将样本数据中的训练集分为小份的训练集和验证集
    """
    param_grid = {
        "solver":["liblinear", "sag", "saga"],
        "max_iter":[100, 150, 200]
    }
    super_estimator = GridSearchCV(estimator, param_grid=param_grid, cv=4)

    # 4.3- 带入数据进行训练
    super_estimator.fit(new_x_train,y_train)

    # 5- 输出交叉验证和网格搜索后的结果
    print("最优的超参数：",super_estimator.best_params_)

    # 交叉验证和网格搜索后的super_estimator可以直接拿着用，也可以自己再重新创建一个
    estimator = LogisticRegression(max_iter=100, solver='liblinear')
    estimator.fit(new_x_train,y_train)

    # 6- 模型评估
    # 6.1- 对测试集数据直接进行内容转换
    new_x_test = transformer.transform(x_test)

    # 6.2- 对测试集数据进行预测
    # y_predict = super_estimator.predict(new_x_test)
    y_predict = estimator.predict(new_x_test)

    # 6.3- 评估
    print("逻辑回归的算法预测评分：", accuracy_score(y_test, y_predict))